import * as Phaser from "phaser";
import {Player} from "../widgets/Player.ts";
import {Target} from "../widgets/Target.ts";
import {Fish} from "../widgets/Fish.ts";
import {NumberBall} from "../widgets/Number.ts";
import {Rule} from "../sys/rule.ts";
import {RatingSystem} from "../sys/rating.ts";
import {MenuPanel} from "../widgets/MenuPanel.ts";
import {SourcePanel} from "@/widgets/SourcePanel.ts";
import WebSocketClient from "@/ws/websocket.ts";
import {GameOverCallback, StartParams} from "@/sys/models.ts";
import {AttentionSystem} from "@/sys/attentions.ts";

export class Ocean1Main extends Phaser.Scene {

    private declare player: Player;
    // 目标、干扰组合
    private declare overGroup: Phaser.Physics.Arcade.Group;
    private declare fishGroup: Phaser.Physics.Arcade.Group;
    private declare numberGroup: Phaser.Physics.Arcade.Group;
    private declare backgroundTile: Phaser.GameObjects.TileSprite;
    private declare targetNumberText: Phaser.GameObjects.Text;
    // private declare testText: Phaser.GameObjects.Text;
    private declare rule: Rule;
    private declare ratingSystem: RatingSystem;
    private declare menuPanel: MenuPanel;

    private readonly targetIndexList: number[];
    private readonly disturbsIndexList: number[];
    // 场景
    private declare readonly sceneType: number;
    // mode 1练习，2评估，3训练
    private declare readonly sceneMode: number;
    private declare readonly overCallback: GameOverCallback;
    private readonly timeCount: number;
    // 目标数字
    private targetNumberMap: Map<number, boolean> = new Map<number, boolean>();
    targetNumber: number = -1;
    targetNumberInArray: boolean = false;
    /**
     * 综合认知能力模式下，排序的下标
     * @private
     */
    private orderlyIndex = 0;
    private declare readonly attention: number;
    private declare sourcePanel: SourcePanel;
    // 注意力列表，用于后面求平均
    private declare readonly attentionArray: number[];
    private declare readonly websocket: WebSocketClient;
    private declare attentionSystem: AttentionSystem;

    constructor(startParams: StartParams) {
        super("Ocean1Main");
        console.log('目标：', startParams.numbers, '干扰', startParams.disturbs)
        this.overCallback = startParams.callback;
        this.attentionArray = [startParams.attention];
        this.targetIndexList = startParams.numbers;
        this.disturbsIndexList = startParams.disturbs;
        this.timeCount = startParams.mode == 1 ? 60 : startParams.mode == 2 ? 120 : 180;
        this.sceneType = startParams.scene;
        this.sceneMode = startParams.mode;
        this.attention = startParams.attention;
        this.attentionSystem = new AttentionSystem(this, (velocity: number) => {
            this.generate(this.rule.generate(), velocity)
        });

        if (this.attention === -1) {
            this.attentionArray = [];
            const host: string = startParams.product ? 'wss://api.naoyouji.com' : 'wss://api-dev.naoyouji.com';
            this.websocket = new WebSocketClient(startParams.uid, startParams.token);
            this.websocket.connect(host);
            this.websocket.setListener((result: number) => {
                if (this.sourcePanel) {
                    this.attentionArray.push(result)
                    this.sourcePanel.updateAttention(result)
                    this.attentionSystem.updateAttention(result);
                }
            })
        }
        this.attentionSystem.updateAttention(this.attention);
    }

    create(): void {
        const {width, height} = this.cameras.main;
        this.physics.world.setBounds(0, 0, width, height)
        this.add.image(0, height, 'BG1').setOrigin(0, 1).setScale(width / 750)
        this.backgroundTile = this.add.tileSprite(0, 0, width, height, 'BG1Tile')
            .setOrigin(0, 0)
            .setTileScale(width / 750);

        this.player = new Player(this);
        this.overGroup = this.physics.add.group();
        this.fishGroup = this.physics.add.group();
        this.numberGroup = this.physics.add.group();
        this.targetNumberText = this.add.text(this.cameras.main.width / 2, 150, '', {
            fontSize: 42,
            fontFamily: 'Arial'
        }).setOrigin(0.5);

        this.rule = new Rule(this.sceneType);
        this.sourcePanel = new SourcePanel(this, this.sceneMode, this.attention, this.timeCount);
        this.ratingSystem = new RatingSystem(this, this.sceneType, this.sourcePanel);
        this.menuPanel = new MenuPanel(this, this.sceneType);

        this.addEvent();

        // this.testText = this.add.text(this.cameras.main.width / 2, 400, '', {
        //     fontSize: 60,
        //     fontFamily: 'Arial'
        // }).setOrigin(0.5);

        this.attentionSystem.start();
    }

    /**
     * 生成目标
     */
    private generateTargets(index: number, velocity: number) {
        let orderly = this.sceneMode == 3 && this.sceneType == 3;
        const target = new Target(this, this.ratingSystem, orderly, 'target', index);
        this.overGroup.add(target);
        target.start(velocity);
    }

    /**
     * 生成干扰
     */
    private generateDisturbs(index: number, velocity: number) {
        let orderly = this.sceneMode == 3 && this.sceneType == 3;
        const target = new Target(this, this.ratingSystem, orderly, 'disturb', index);
        this.overGroup.add(target);
        target.start(velocity);
    }

    /**
     * 生成鱼
     * @private
     */
    private generateFish(velocity: number) {
        if (this.fishGroup.getChildren().length > 0) {
            this.generate(Phaser.Math.Between(0, 1), velocity)
            return;
        }
        let fish: Fish = new Fish(this, this.ratingSystem);
        this.fishGroup.add(fish)
        fish.start(velocity);
    }

    /**
     * 生成数字球
     * @private
     */
    private generateNumbers(velocity: number) {
        if (this.numberGroup.getChildren().length > 0) {
            this.generate(Phaser.Math.Between(0, 2), velocity)
            return;
        }
        this.targetNumberMap.clear();
        const {numbers, target, targetInArray} = this.rule.generateArrayAndTarget(4);
        this.targetNumberInArray = targetInArray;
        this.targetNumber = target;
        this.targetNumberText.text = `目标：${target}`;
        for (let i = 0; i < numbers.length; i++) {
            const numIndex = numbers[i] - 1;
            let ball: NumberBall = new NumberBall(this, this.ratingSystem, 'SpritesNumber', i, numIndex);
            this.numberGroup.add(ball);
            this.targetNumberMap.set(numIndex, false);
            ball.start(velocity);
        }
    }

    private generate(order: number, velocity: number) {
        const index = Math.floor(Math.random() * this.targetIndexList.length);
        const targetIndex = this.targetIndexList[index]; // 5,7,15
        const disturbIndex = this.disturbsIndexList[index]; // 5,7,15
        switch (order) {
            case 0:
                this.generateTargets(targetIndex, velocity);
                break;
            case 1:
                this.generateDisturbs(disturbIndex, velocity);
                break;
            case 2:
                this.generateNumbers(velocity);
                break;
            case 3:
                this.generateFish(velocity);
                break;
        }
    }

    /**
     * 碰撞消除目标
     * @param _
     * @param target
     * @private
     */
    private overlap(_: any, target: any) {
        const tar = target as Target;
        if (this.sceneType == 3 && tar.type == 'target') {
            // 按顺序收集，当消除的目标是目标数组的数序值，则正确，否则错误。
            if (this.targetIndexList[this.orderlyIndex] == tar.index) {
                tar.overlap();
                this.orderlyIndex++;
                // 重复新的轮回
                if (this.orderlyIndex == this.targetIndexList.length) {
                    this.orderlyIndex = 0;
                }
            } else {
                tar.overlapTargetUnSpecified();
            }
            return
        }
        tar.overlap();
    }

    /**
     * 碰撞数字
     * @param _
     * @param number
     * @private
     */
    private overlapNumber(_: any, number: any) {
        const ball = number as NumberBall;
        this.targetNumberMap.set(ball.id, true);
        ball.overlap();
    }

    addEvent() {
        this.time.delayedCall(1000 * this.timeCount, () => {
            this.gameOver();
        });
        this.physics.add.overlap(this.player, this.overGroup, this.overlap, undefined, this);
        this.physics.add.overlap(this.player, this.numberGroup, this.overlapNumber, undefined, this);
    }

    resumeMusic(hide: boolean) {
        console.log('hide', hide)
        // this.menuPanel && this.menuPanel.resumeMusic(hide);
    }

    gameOver() {
        if (this.attention === -1 && this.websocket) {
            this.websocket.close();
        }
        const source = this.ratingSystem.getSource();
        source.attention = this.getRoundedAverage(this.attentionArray);
        this.overCallback(source)
        this.sys.pause();
        this.sound.pauseAll();
        this.time.removeAllEvents();
    }

    update() {
        this.backgroundTile.tilePositionY -= 1;
        this.updateForNumbers();
    }

    private updateForNumbers() {
        let allReached = true;
        this.numberGroup.getChildren().forEach((child: Phaser.GameObjects.GameObject) => {
            const ball = child as NumberBall;
            if (ball.y < this.cameras.main.height) {
                allReached = false;
            }
        });
        if (allReached && this.targetNumber != -1) { // 全部数字消失 且 有一排数字下落
            let overlapCount = 0; // 撞击个数
            this.targetNumberMap.forEach((value) => {
                if (value) overlapCount += 1;
            });
            if (overlapCount == 0) {
                if (this.targetNumberInArray) {// 目标数字在数组中，且没有碰撞，反应失败
                    this.ratingSystem.reactionError(true);
                } else {// 目标数字不在数组中，且没有碰撞，抑制成功
                    this.ratingSystem.restrainRight();
                }
            }
            this.targetNumber = -1;
            this.targetNumberText.text = '';
        }
    }

    private getRoundedAverage(arr: number[]): number {
        if (!arr.length) return 0
        const avg = arr.reduce((a, b) => a + b, 0) / arr.length
        return Number(avg.toFixed(0))
    }
}